﻿/*
 * TrackerWebRequest
 * 
 * Author: snowdreamist
 * Date: 2011/11/18
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net;
using System.Threading;
using BitTorrentSharp.Torrent;

namespace BitTorrentSharp.Tracker
{
    public enum TrackerEvent
    {
        Started,
        Completed,
        Stopped,
        Empty
    }

    public class TrackerWebRequest
    {
        /// <summary>
        /// create a empty tracker web request
        /// </summary>
        public TrackerWebRequest() { }
        /// <summary>
        /// create tracker web request by httpwebrequest
        /// </summary>
        /// <param name="request"></param>
        public TrackerWebRequest(HttpWebRequest request)
        {
            if (request == null)
                throw new ArgumentNullException("request");
            this.ParseHttpWebRequest(request);
        }

        public TrackerWebResponse GetResponse()
        {
            return new TrackerWebResponse(this.CreateHttpWebRequest().GetResponse() as HttpWebResponse);
        }

        public IAsyncResult BeginGetResponse(AsyncCallback callback, object state)
        {
            throw new NotImplementedException();
        }

        public TrackerWebResponse EndGetResponse(IAsyncResult asyncResult)
        {
            throw new NotImplementedException();
        }
        
        #region properties
        /// <summary>
        /// announce address
        /// </summary>
        public Uri AnnountUri { get; set; }
        /// <summary>
        /// SHA1 hash in info
        /// </summary>
        public byte[] InfoHash { get; set; }
        /// <summary>
        /// id of this peer
        /// should be only contains ACSII chars
        /// </summary>
        public string PeerID { get; set; }
        /// <summary>
        /// ip or dns of this client
        /// </summary>
        public string IPOrDNS { get; set; }
        /// <summary>
        /// port of this client
        /// </summary>
        public int Port { get; set; }
        /// <summary>
        /// enable compress or not
        /// </summary>
        public bool EnableCompact{ get; set; }
        /// <summary>
        /// total bytes upload
        /// </summary>
        public long Uploaded { get; set; }
        /// <summary>
        /// total bytes download
        /// </summary>
        public long Downloaded { get; set; }
        /// <summary>
        /// total bytes need to download
        /// </summary>
        public long Left { get; set; }
        /// <summary>
        /// current event
        /// </summary>
        public TrackerEvent? Event { get; set; }
        /// <summary>
        /// Peers want to achieve
        /// </summary>
        public int PeersCountWanted { get; set; }
        /// <summary>
        /// Client Key [Optional]
        /// If client want to keep its identity when ip changes,
        /// please set this property to a unique value
        /// ATTENTION:
        ///     Length limition is unknown
        /// </summary>
        public byte[] Key { get; set; }
        /// <summary>
        /// TrackerID [Optional]
        /// </summary>
        public byte[] TrackerID { get; set; }

        #endregion

        #region consts

        internal const string InfoHashKey = "info_hash";
        internal const string PeerIDKey = "peer_id";
        internal const string IPKey = "ip";
        internal const string PortKey = "port";
        internal const string UploadedKey = "uploaded";
        internal const string DownloadedKey = "downloaded";
        internal const string LeftKey = "left";
        internal const string EventKey = "event";
        internal const string CompactKey = "compact";
        internal const string PeerNumberWanted = "numwant";
        internal const string KeyKey = "key";
        internal const string TrackerIDKey = "trackerid";

        #endregion

        #region private

        private void ParseHttpWebRequest(HttpWebRequest request)
        {
            throw new NotImplementedException();
        }

        private Dictionary<string,byte[]> BuildHttpWebRequestParameters()
        {
            if (this.AnnountUri == null)
                throw new ArgumentNullException("announce uri");
            if (this.InfoHash == null || this.InfoHash.Length != 20)
                throw new ArgumentException("Unavailable info hash");
            if (!PeerIDUtility.VerifyPeerID(this.PeerID))
                throw new ArgumentException("Unavailable peer id");

            Dictionary<string, byte[]> paraDict = new Dictionary<string, byte[]>();
            // info hash
            paraDict[TrackerWebRequest.InfoHashKey] = this.InfoHash;
            // peer id
            paraDict[TrackerWebRequest.PeerIDKey] = Encoding.Default.GetBytes(this.PeerID);
            // ip
            if (!string.IsNullOrWhiteSpace(this.IPOrDNS))
            {
                paraDict[TrackerWebRequest.IPKey] = Encoding.Default.GetBytes(this.IPOrDNS);
            }
            // port
            paraDict[TrackerWebRequest.PortKey] = Encoding.Default.GetBytes(this.Port.ToString());
            // enableCompress
            paraDict[TrackerWebRequest.CompactKey] = Encoding.Default.GetBytes(this.EnableCompact ? "1" : "0");
            // uploaded
            paraDict[TrackerWebRequest.UploadedKey] = Encoding.Default.GetBytes(this.Uploaded.ToString());
            // downloaded
            paraDict[TrackerWebRequest.DownloadedKey] = Encoding.Default.GetBytes(this.Downloaded.ToString());
            // left
            paraDict[TrackerWebRequest.LeftKey] = Encoding.Default.GetBytes(this.Left.ToString());
            // event
            if (this.Event != null)
            {
                paraDict[TrackerWebRequest.EventKey] = Encoding.Default.GetBytes(this.Event.ToString().ToLower());
            }
            // peer count wanted
            paraDict[TrackerWebRequest.PeerNumberWanted] = Encoding.Default.GetBytes(this.PeersCountWanted.ToString());
            // key
            if (this.Key != null)
            {
                paraDict[TrackerWebRequest.KeyKey] = this.Key;
            }
            // tracker id
            if (this.TrackerID != null)
            {
                paraDict[TrackerWebRequest.TrackerIDKey] = this.TrackerID;
            }

            return paraDict;
        }

        internal HttpWebRequest CreateHttpWebRequest()
        {
            var queryParameter = this.BuildHttpWebRequestParameters();
            string absoluteUri = null;

            if (queryParameter.Count == 0)
            {
                absoluteUri = this.AnnountUri.AbsoluteUri;
            }
            else
            {
                StringBuilder uriBuilder = new StringBuilder();
                uriBuilder.Append(this.AnnountUri.AbsoluteUri.ToString());
                uriBuilder.Append('?');

                bool isFirst = true;

                foreach (var keyValuePair in queryParameter)
                {
                    if (!isFirst)
                        uriBuilder.Append('&');
                    else
                        isFirst = false;
                    uriBuilder.Append(HttpUtility.UrlEncode(keyValuePair.Key));
                    uriBuilder.Append('=');
                    uriBuilder.Append(HttpUtility.UrlEncode(keyValuePair.Value));
                }


                absoluteUri = uriBuilder.ToString();
#if DEBUG
                System.Diagnostics.Debug.WriteLine(string.Format("TrackerWebRequest Uri: {0}", absoluteUri), "TrackerWebRequest");
#endif
            }

            HttpWebRequest request = HttpWebRequest.Create(new Uri(absoluteUri, UriKind.Absolute)) as HttpWebRequest;

            request.Method = "GET";

            return request;
        }
        #endregion
    }
}
